package guyubao.com.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import guyubao.com.entity.Users;
import guyubao.com.entity.dto.AccountDto;
import guyubao.com.entity.vo.request.*;
import guyubao.com.mapper.AccountMapper;
import guyubao.com.service.AccountService;
import guyubao.com.service.RedisService;
import guyubao.com.utils.Const;
import guyubao.com.utils.FlowUtils;
import guyubao.com.utils.SnowflakeIdGenerator;
import javax.annotation.Resource;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, AccountDto> implements AccountService {
    //验证邮件发送冷却时间限制，秒为单位
    @Value("${spring.web.verify.mail-limit}")
    int verifyLimit;
    @Resource
    AmqpTemplate amqpTemplate;
    @Resource
    RedisService redisService;
    @Resource
    AccountMapper accountMapper;
    @Resource
    FlowUtils flowUtils;
    @Resource
    PasswordEncoder passwordEncoder;
    @Resource
    SnowflakeIdGenerator snowflakeIdGenerator;
    /***
     * 登录
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        AccountDto accountDto = this.findAccountByNameOrEmail(username);
        if (accountDto == null) throw new UsernameNotFoundException("用户名或密码错误");
        return Users
                .withUsername(username)
                .id(accountDto.getId())
                .name(accountDto.getUsername())
                .email(accountDto.getEmail())
                .role(accountDto.getRole())
                .password(accountDto.getPassword())
                .roles(accountDto.getRole())
                .build();
    }
    /**
     * 通过邮件地址查找用户
     * @param text 用户名或邮件
     * @return 账户实体
     */
    public AccountDto findAccountByNameOrEmail(String text){
        return this.query()
                .eq("username",text).or()
                .eq("email",text)
                .one();
    }

    @Override
    public AccountDto findAccountById(String id) {
        return this.query()
                .eq("id",id)
                .one();
    }

    /***
     * 发送邮件
     */
    @Override
    public String sendEmailVerifyCode(String type, String email, String ip) {
        // 对请求ip和邮箱地址进行限制
        synchronized (ip.intern()){
            synchronized (email.intern()){
                if (!this.verifyLimit(ip,email,type)) {
                    return "请求频繁，请稍后再试";
                }
                // 生成验证码
                Random random = new Random();
                int code = random.nextInt(899999)+100000;
                // 放入消息队列
                HashMap<String,Object> data = new HashMap<>();
                data.put("type",type);
                data.put("email",email);
                data.put("code",code);
                amqpTemplate.convertAndSend(Const.MQ_MAIL,data);
                // 将验证码放入redis
                redisService.putEmailVerifyCode(type+":"+email,code,10*60);
                return null;
            }
        }
    }
    /***
     * 发送邮件
     * @param type 类型
     * @param otherKey 数据库列名
     * @param otherValue 数据库值
     * @param ip 发送ip地址
     */
    @Override
    public String sendEmailVerifyCodeOther(String type, String otherKey,String otherValue, String ip) {
        AccountDto accountDto = this.query().eq(otherKey, otherValue).one();
        if (accountDto==null) {
            return "用户名或邮箱不存在";
        }
        String email = accountDto.getEmail();
        if (email==null|| email.isEmpty()) {
            return "邮件不存在";
        }
        return this.sendEmailVerifyCode(type,email,ip);
    }

    /**
     * 用户注册
     */
    @Override
    public String registerEmailAccount(EmailRegisterVoRequest vo) {
        String email = vo.getEmail();
        String username = vo.getUsername();
        // 判断是否有重复
        if (this.existsLoginByEmail(email)||this.existsLoginByEmail(username)) {
            return "此电子邮箱已被其他用户注册,请重新输入电子邮箱提交";
        }
        if (this.existsLoginByUsername(username)||this.existsLoginByUsername(email)) {
            return "此用户名已被其他用户注册,请重新输入用户名提交";
        }
        // 验证码与redis验证码判断
        String redisCode = redisService.verificationCode(vo.getCode(), "register", email);
        if (redisCode!=null) {
            return  redisCode;
        }
        // 密码加密并保存
        String password = passwordEncoder.encode(vo.getPassword());
        AccountDto accountDto = new AccountDto(String.valueOf(snowflakeIdGenerator.nextId()),username,password,email,null,null,null);
        return this.save(accountDto) ? null :"内部错误请联系管理人员";
    }

    /***
     * 重置密码：确认邮箱
     * @param vo 验证基本信息
     * @return 操作结果，null表示正常，否则为错误原因
     */
    @Override
    public String resetConfirm(ConfirmResetVoRequest vo) {
        String email = vo.getEmail();
        String code = redisService.getEmailVerifyCode("resetPassword",email);
        if (code==null)return "请先获取验证码";
        if (!code.equals(vo.getCode())){
            redisService.deleteEmailVerifyCode("resetPassword",email);
            return "验证码输入错误，请重新获取验证码";
        }
        return null;
    }

    /**
     * 邮件验证码重置密码操作，需要检查验证码是否正确
     * @param vo 重置基本信息
     * @return 操作结果，null表示正常，否则为错误原因
     */
    @Override
    public String resetEmailAccountPassword(EmailResetVoRequest vo) {
        // 看看验证码是否正确
        String verify = this.resetConfirm(new ConfirmResetVoRequest(vo.getEmail(), vo.getCode()));
        if (verify!=null) return verify;
        redisService.deleteEmailVerifyCode("resetPassword",vo.getEmail());
        // 开始修改密码
        String email = vo.getEmail();
        String password = passwordEncoder.encode(vo.getPassword());
        boolean update = this.update().eq("email",email).set("password",password).update();
        return update ? null : "更新失败，请联系管理员";
    }

    /***
     * 重置邮箱
     */
    @Override
    public String resetEmail(ResetEmailVoRequest vo) {
        return this.update().set("email", vo.getEmail()).eq("id", vo.getId()).update() ?null:"重置邮箱失败，请联系管理人员";
    }

    /***
     * 重置用户名
     */
    @Override
    public String resetUserName(ResetUsernameVoRequest vo) {
        return this.update().set("username", vo.getUsername()).eq("id", vo.getId()).update() ?null:"重置用户名失败，请联系管理人员";
    }

    /***
     * 解除账号
     */
    @Override
    public String deleteOneself(String id) {
        return accountMapper.deleteAccount(id)!=0?null:"数据库删除用户失败，请联系管理人员！";
    }

    /***
     * 获得用户封禁时间
     */
    @Override
    public Date getDisableTimeById(String id) {
        return this.query().eq("id",id).one().getDisabledTime();
    }

    /***
     * 封印用户
     * @param id
     * @return
     */
    @Override
    public String disableAccount(String id,int hour) {
        Date date = new Date();
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR,hour);
        Date time = instance.getTime();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return this.update().set("disabledTime",formatter.format(time))
                .eq("id",id)
                .update()?null:"数据库封禁用户失败,开始改代码";
    }

    @Override
    public String ableAccount(String id) {
        return this.update().set("disabledTime",null)
                .eq("id",id)
                .update()?null:"数据库解封用户失败,开始改代码";
    }

    /***
     * 管理员获取所有用户
     */
    @Override
    public List<AccountDto> getAllAccount() {
        return this.query().list();
    }

    /***
     * 管理员获取所有用户以分页方式返回
     */
    @Override
    public Page<AccountDto> getAllAccpuntPage(long size,long current,String column,String val) {
        // 设置分页属性
        Page<AccountDto> page = new Page<>();
        // 当页条数
        if (size>0) {
            page.setSize(size);
        }
        // 当前页数
        if (current>0) {
            page.setCurrent(current);
        }
        QueryWrapper<AccountDto> query = new QueryWrapper<>();
        if (column!=null&&val!=null&&!column.isEmpty()&&!val.isEmpty()) {
            query.like(column, val);
        }
        return accountMapper.selectPage(page,query);
    }

    /**
     * 查询指定邮箱的用户是否已经存在
     * @param email 邮箱
     * @return 是否存在
     */
    private boolean existsLoginByEmail(String email){
        return this.baseMapper.selectOne(Wrappers.<AccountDto>query().eq("email", email)) != null;
    }
    /**
     * 查询指定用户名的用户是否已经存在
     * @param username 用户名
     * @return 是否存在
     */
    private boolean existsLoginByUsername(String username){
        return this.baseMapper.selectOne(Wrappers.<AccountDto>query().eq("username", username)) != null;
    }
    /**
     * 针对IP地址和邮箱进行邮件验证码获取限流
     * @param ip 地址
     * @param email 邮箱
     * @return 是否通过验证
     */
    private boolean verifyLimit(String ip,String email,String type){
        String key = Const.VERIFY_EMAIL_LIMIT+type+":" + ip;
        String key2 = Const.VERIFY_EMAIL_LIMIT+type+":" + email;
        return flowUtils.limitOnceCheck(key,verifyLimit) && flowUtils.limitOnceCheck(key2,verifyLimit);
    }
}
